package com.zxjbyte.yiyi.module.system.service.oss.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zxjbyte.yiyi.framework.common.domain.KVStore;
import com.zxjbyte.yiyi.framework.common.domain.api.PageResult;
import com.zxjbyte.yiyi.framework.common.util.JsonUtil;
import com.zxjbyte.yiyi.framework.common.util.ValidationUtil;
import com.zxjbyte.yiyi.framework.mybatis.service.impl.BaseServiceXImpl;
import com.zxjbyte.yiyi.framework.mybatis.util.Condition;
import com.zxjbyte.yiyi.framework.oss.enums.OssStorageEnum;
import com.zxjbyte.yiyi.framework.oss.storage.OssConfig;
import com.zxjbyte.yiyi.framework.redis.manager.RedisDao;
import com.zxjbyte.yiyi.module.system.convert.OssConfigConvert;
import com.zxjbyte.yiyi.module.system.domain.entity.OssConfigPO;
import com.zxjbyte.yiyi.module.system.domain.param.OssConfigAddParam;
import com.zxjbyte.yiyi.module.system.domain.param.OssConfigListParam;
import com.zxjbyte.yiyi.module.system.domain.param.OssConfigUpdateParam;
import com.zxjbyte.yiyi.module.system.domain.vo.OssConfigListVO;
import com.zxjbyte.yiyi.module.system.mapper.OssConfigMapper;
import com.zxjbyte.yiyi.module.system.messaging.consumer.OssConfigRefreshConsumer;
import com.zxjbyte.yiyi.module.system.service.oss.OssConfigService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;

import static com.zxjbyte.yiyi.framework.common.enums.SortOrder.DESC;

/**
 * @Author zhangxingjia
 * @Date 2023/5/11 16:16
 * @Version: 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OssConfigServiceImpl extends BaseServiceXImpl<OssConfigMapper, OssConfigPO> implements OssConfigService {

    private final RedisDao redisDao;

    @Override
    public OssConfigPO getMasterConfigByStorageCode(String storageCode) {
        return lambdaQuery().eq(OssConfigPO::getStorageCode, storageCode).eq(OssConfigPO::getMaster, true).one();
    }

    @Override
    public PageResult<OssConfigListVO> list(OssConfigListParam param) {
        LambdaQueryWrapper<OssConfigPO> wrapper = Wrappers.<OssConfigPO>lambdaQuery()
                .eq(Objects.nonNull(param.getStorageCode()), OssConfigPO::getStorageCode, param.getStorageCode())
                .like(StrUtil.isNotBlank(param.getName()), OssConfigPO::getName, param.getName())
                .ge(Objects.nonNull(param.getStartCreateTime()), OssConfigPO::getCreateTime, param.getStartCreateTime())
                .le(Objects.nonNull(param.getEndCreateTime()), OssConfigPO::getCreateTime, param.getEndCreateTime());
        IPage<OssConfigPO> page = page(Condition.getPage(param, OssConfigPO::getCreateTime, DESC), wrapper);
        List<OssConfigListVO> voList = OssConfigConvert.INSTANCE.configPoListToVoList(page.getRecords());
        return PageResult.of(voList, page);
    }

    @Override
    public void add(OssConfigAddParam param) {
        OssConfig ossConfig = buildOssConfig(param.getConfig(), param.getStorageCode());
        OssConfigPO ossConfigPo = new OssConfigPO();
        ossConfigPo.setName(param.getName());
        ossConfigPo.setMaster(false);
        ossConfigPo.setStorageCode(param.getStorageCode());
        ossConfigPo.setConfig(ossConfig);
        save(ossConfigPo);
    }

    @Override
    public void update(OssConfigUpdateParam param) {
        OssConfigPO ossConfigPo = checkById(param.getId());
        OssConfig ossConfig = buildOssConfig(param.getConfig(), ossConfigPo.getStorageCode());
        update(new OssConfigPO().setConfig(ossConfig), Wrappers.<OssConfigPO>lambdaUpdate().set(OssConfigPO::getName, param.getName())
                .eq(OssConfigPO::getId, param.getId()));
        // 编辑主配置时重新reload配置信息
        if(ossConfigPo.getMaster()){
            sendRefresh(ossConfigPo.getStorageCode());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeMaster(Long id) {
        OssConfigPO ossConfigPo = checkById(id);
        ValidationUtil.assertFalse(ossConfigPo.getMaster(), "当前配置已经是主配置，请勿重复操作");
        // 每个存储器的主配置有且只有一个
        update(Wrappers.<OssConfigPO>lambdaUpdate().set(OssConfigPO::getMaster, true)
                .eq(OssConfigPO::getId, id));
        sendRefresh(ossConfigPo.getStorageCode());
    }

    @Override
    public void delete(Long id) {
        OssConfigPO ossConfigPo = checkById(id);
        ValidationUtil.assertFalse(ossConfigPo.getMaster(), "该oss配置为主配置不可删除，删除会导致无法上传文件");
        removeById(id);
    }

    /**
     * 执行Pub/Sub模式异步消息传递
     * @param storageCode
     */
    private void sendRefresh(String storageCode){
        redisDao.publish(OssConfigRefreshConsumer.CHANNEL_SYSTEM_OSS_CONFIG_REFRESH, storageCode);
    }


    private OssConfigPO checkById(Long id){
        OssConfigPO ossConfigPo = null;
        ValidationUtil.assertFalse(id <= 0 || (ossConfigPo = getById(id)) == null, "oss配置不存在");
        return ossConfigPo;
    }

    private OssConfig buildOssConfig(KVStore config, String storageCode){
        String configJsonStr = config.toJsonStr();
        Class<? extends OssConfig> configClass = OssStorageEnum.getByStorageCode(storageCode).getConfigClass();
        OssConfig ossConfig = JsonUtil.parseObject2(configJsonStr, configClass);
        ValidationUtil.validate(ossConfig);
        return ossConfig;
    }
}
